// Youdao TTS signing and request utility

function sha256Hex(input: string): Promise<string> {
  const enc = new TextEncoder();
  const data = enc.encode(input);
  return crypto.subtle.digest('SHA-256', data).then(buf =>
    Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('')
  );
}

export type TtsOptions = {
  text: string;
  appKey: string; // youdao app id
  appSecret: string; // youdao secret
  voiceName?: string; // e.g. 'youxiaozhi'
  format?: 'mp3' | 'wav';
  speed?: string; // "1.2" etc
  volume?: string; // "1.0"
  pitch?: string; // "0.0"
};

export async function synthesizeYoudao(opts: TtsOptions): Promise<Blob> {
  const truncate = (s: string): string => {
    if (s.length <= 20) return s;
    return s.slice(0, 10) + String(s.length) + s.slice(-10);
  };

  const attempt = async (signUseEncoded: boolean, useQParam: boolean, useTruncate: boolean) => {
    const salt = crypto.randomUUID();
    const curtime = Math.floor(Date.now() / 1000).toString();
    const inputRaw = opts.text;
    const inputEncoded = encodeURIComponent(inputRaw);
    const base = signUseEncoded ? inputEncoded : inputRaw;
    const signInput = useTruncate ? truncate(base) : base;
    const signStr = opts.appKey + signInput + salt + curtime + opts.appSecret;
    const sign = await sha256Hex(signStr);

    const form = new URLSearchParams();
    form.set('appKey', opts.appKey);
    form.set('salt', salt);
    form.set('signType', 'v3');
    form.set('curtime', curtime);
    form.set('sign', sign);
    const textKey = useQParam ? 'q' : 'input';
    // 表单里放原始文本（浏览器会自动进行urlencoded），避免与服务端验签的原文不一致
    form.set(textKey, inputRaw);
    form.set('format', opts.format ?? 'mp3');
    if (opts.voiceName) form.set('voiceName', opts.voiceName);
    if (opts.speed) form.set('speed', opts.speed);
    if (opts.volume) form.set('volume', opts.volume);
    if (opts.pitch) form.set('pitch', opts.pitch);

    const res = await fetch('/tts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Accept': 'audio/mp3,application/json' },
      body: form.toString(),
    });

    const contentType = res.headers.get('Content-Type') || '';
    if (!res.ok) {
      const text = await res.text();
      throw new Error(`TTS HTTP ${res.status}: ${text}`);
    }
    if (contentType.includes('application/json')) {
      const json = await res.json();
      if (json && json.errorCode && json.errorCode !== '0') {
        const err: any = new Error(`TTS error: ${JSON.stringify(json)}`);
        err.code = json.errorCode;
        err.requestId = json.requestId;
        throw err;
      }
      if (json && json.audioBase64) {
        const bytes = Uint8Array.from(atob(json.audioBase64), c => c.charCodeAt(0));
        return new Blob([bytes], { type: 'audio/mp3' });
      }
      throw new Error(`TTS error: unexpected JSON ${JSON.stringify(json)}`);
    }
    return await res.blob();
  };

  try {
    // 尝试1：签名用编码后的 input 参数名 input，不截断
    return await attempt(true, false, false);
  } catch (e: any) {
    if (e && (e.code === '113' || /113/.test(String(e.message)))) {
      try {
        // 尝试2：签名用原始未编码 input 参数名 input，不截断
        return await attempt(false, false, false);
      } catch (e2: any) {
        if (e2 && (e2.code === '113' || /113/.test(String(e2.message)))) {
          // 尝试3：参数名改为 q，签名对原始文本（不少平台q参与签名），并按有道v3建议对input进行截断
          return await attempt(false, true, true);
        }
        throw e2;
      }
    }
    throw e;
  }
}


// Helper: synthesize and immediately drive mouth from the returned audio
export async function synthesizeAndDriveYoudao(opts: TtsOptions): Promise<Blob> {
  const blob = await synthesizeYoudao(opts);
  try {
    const fn: any = (window as any).driveMouthFromBlob;
    if (typeof fn === 'function') {
      fn(blob);
    }
  } catch {}
  return blob;
}


